Imports System.Runtime.InteropServices
Imports System.IO
Imports System.Net.Security

Friend Module mMirMapFile

    <StructLayout(LayoutKind.Explicit, Size:=14)> _
    Public Structure RawSmallTile
        <FieldOffset(0)> <MarshalAs(UnmanagedType.I1)> Dim passable As Byte '2= blocked 3= passable
        <FieldOffset(1)> <MarshalAs(UnmanagedType.I1)> Dim Ani1 As Byte
        <FieldOffset(2)> <MarshalAs(UnmanagedType.I1)> Dim Ani2 As Byte
        <FieldOffset(3)> <MarshalAs(UnmanagedType.I1)> Dim TopSet As Byte
        <FieldOffset(4)> <MarshalAs(UnmanagedType.I1)> Dim BottomSet As Byte
        <FieldOffset(5)> <MarshalAs(UnmanagedType.I2)> Dim BottomTile As UInt16
        <FieldOffset(7)> <MarshalAs(UnmanagedType.I2)> Dim TopTile As UInt16
        <FieldOffset(9)> <MarshalAs(UnmanagedType.I2)> Dim Door As UInt16
        <FieldOffset(11)> <MarshalAs(UnmanagedType.I1)> Dim DoorOff As Byte
        <FieldOffset(12)> <MarshalAs(UnmanagedType.I2)> Dim Light As UInt16
    End Structure

    Public TileLibLookup() As Integer = {EvoLib.Protocol.ImgLibs.TileSC, _
                                    EvoLib.Protocol.ImgLibs.TileS30C, _
                                    EvoLib.Protocol.ImgLibs.Tiles5C, _
                                    EvoLib.Protocol.ImgLibs.SmTileSC, _
                                    EvoLib.Protocol.ImgLibs.HouseSC, _
                                    EvoLib.Protocol.ImgLibs.CliffSC, _
                                    EvoLib.Protocol.ImgLibs.DungeonSC, _
                                    EvoLib.Protocol.ImgLibs.InnerSC, _
                                    EvoLib.Protocol.ImgLibs.FurnitureSC, _
                                    EvoLib.Protocol.ImgLibs.WallSC, _
                                    EvoLib.Protocol.ImgLibs.SMObjectSC, _
                                    EvoLib.Protocol.ImgLibs.AnimationSC, _
                                        EvoLib.Protocol.ImgLibs.Objects1C, _
                                         EvoLib.Protocol.ImgLibs.Object2C, _
                                         0, _
                                         EvoLib.Protocol.ImgLibs.WoodTileSC, _
                                         0, _
                                         EvoLib.Protocol.ImgLibs.WoodTiles5C, _
                                         0, _
                                         EvoLib.Protocol.ImgLibs.WoodHouseSC, _
                                         EvoLib.Protocol.ImgLibs.WoodCliffSC, _
                                         EvoLib.Protocol.ImgLibs.WoodDungeonSC, _
                                         EvoLib.Protocol.ImgLibs.WoodInnerSC, _
                                    EvoLib.Protocol.ImgLibs.WoodFurnitureSC, _
                                    0, _
                                    EvoLib.Protocol.ImgLibs.WoodSMObjectSC}

    Public Structure LargeTile
        Dim TileSet As Byte
        Dim Index As UInt16
    End Structure

    Public Structure SmallTile
        Dim IsPassable As Boolean
        Dim TopTileSet As Byte
        Dim TopTileIndex As UInt16
        Dim BottomTileSet As Byte
        Dim BottomTileIndex As UInt16
        Dim Ani1 As Byte
        Dim Ani2 As Byte
        Dim Door As UInt16
        Dim DoorOff As Byte
        Dim Light As UInt16
    End Structure

    Public Structure Tile
        Dim TileSet As Byte
        Dim TileIndex As UInt16
    End Structure

    Public Function GetImgLib(ByRef tileset As Byte) As Integer

        Return TileLibLookup(tileset)

    End Function

End Module

Friend Class cMirMapFile

    Public Function GetTile(ByRef x As Integer, ByRef y As Integer) As SmallTile


        If Not IsCoordOnMap(x, y) Then
            Dim tmp As SmallTile
            tmp.TopTileSet = 255
            tmp.TopTileIndex = 65535
            tmp.BottomTileSet = 255
            tmp.BottomTileIndex = 65535
            Return tmp
        End If

        Return mSmallTiles(x, y)

    End Function

    'Public Function GetTopTile(ByRef x As Integer, ByRef y As Integer) As Tile

    'Dim tmp As Tile
    'tmp.TileSet = 255
    'tmp.TileIndex = 65535

    'If Not IsCoordOnMap(x, y) Then
    'Return tmp
    'End If

    'With mSmallTiles(x, y)

    'tmp.TileSet = .TopTileSet
    'tmp.TileIndex = .TopTileIndex
    'End With

    'Return tmp

    'End Function

    Public Function GetLargeTile(ByRef x, ByRef y) As LargeTile


        If Not IsCoordOnMap(x, y) Then
            Dim tmp As LargeTile
            tmp.TileSet = 255
            tmp.Index = 65535
            Return tmp
        End If

        Return mLargeTiles(x / 2, y / 2)

    End Function

    Private Function IsCoordOnMap(ByRef x As Integer, ByRef y As Integer) As Boolean

        If x < 0 Or y < 0 Or x >= mWidth Or y >= mHeight Then
            Return False
        Else
            Return True
        End If

    End Function

    Public Function IsPassable(ByRef x As Integer, ByRef y As Integer) As Boolean

        If IsCoordOnMap(x, y) Then
            If mSmallTiles(x, y).IsPassable Then
                For Each tmpchar As Actor.MirActor In Characters.lstCharacters
                    If tmpchar.VitalPoints.HP > 0 Then
                        If tmpchar.mXCoord = x And tmpchar.mYCoord = y Then
                            Return False
                        End If
                    End If
                Next
                For Each tmpmob As Mobs.Mob In Mobs.lstMobs
                    If tmpmob.VitalPoints.HP > 0 Then
                        If tmpmob.PositionInfo.INT2_X = x And tmpmob.PositionInfo.INT2_Y = y Then
                            Return False
                        End If
                    End If
                Next
                Return True
            End If
            Return False
        Else
            Return False
        End If

    End Function

    Private mMap As String
    Private mMapFile As String

    Public mWidth As Integer, mHeight As Integer

    Dim mLargeTiles(,) As LargeTile
    Dim mSmallTiles(,) As SmallTile

    Public Sub New(ByRef map As String)

        mMap = map
        mMapFile = Globals.sMapPath & map & ".map"

        LoadMapData()

    End Sub

    Private Sub LoadMapData()

        'open the map file
        Dim mapStream As New FileStream(mMapFile, FileMode.Open, FileAccess.Read)
        Dim bBuffer(1) As Byte
        Dim itX As Integer, itY As Integer

        'Skip header bytes
        mapStream.Position = 22

        'read map dimensions
        mapStream.Read(bBuffer, 0, 2)
        mWidth = EvoLib.Protocol.Functions.ArrayToStruct(bBuffer, GetType(UInt16))
        mapStream.Read(bBuffer, 0, 2)
        mHeight = EvoLib.Protocol.Functions.ArrayToStruct(bBuffer, GetType(UInt16))

        'skip 2 bytes, don't know what these do atm, maybe related to tile sets
        mapStream.Position += 2

        '=== LARGE TILES ===

        Dim tmpLargeTile As LargeTile
        Dim buffPos As Integer

        ReDim mLargeTiles((mWidth \ 2) - 1, (mHeight \ 2) - 1)
        ReDim bBuffer(((mWidth \ 2) * (mHeight \ 2) * 3) - 1)

        mapStream.Read(bBuffer, 0, (((mWidth \ 2) * (mHeight \ 2)) * 3))

        For itX = 0 To ((mWidth \ 2) - 1)
            For itY = 0 To ((mHeight \ 2) - 1)

                'create new tile structure
                tmpLargeTile = New LargeTile

                'read the tile into structure
                tmpLargeTile.TileSet = bBuffer(buffPos)
                tmpLargeTile.Index = EvoLib.Protocol.Functions.ArrayToStruct(bBuffer, GetType(UInt16), buffPos + 1)

                'if tile set is invalid set tile index to max
                If tmpLargeTile.TileSet = 255 Then
                    tmpLargeTile.Index = 65535
                Else
                    CheckMissingTileSet(tmpLargeTile.TileSet, TileSetType.Large)
                End If

                '(compatibility) if tile is valid add 1 to the index
                If tmpLargeTile.Index < 65535 Then
                    tmpLargeTile.Index += 1
                End If

                mLargeTiles(itX, itY) = tmpLargeTile
                'preloads tiles
                'If tmpLargeTile.TileSet < 255 Then Libs.GetLib(GetImgLib(tmpLargeTile.TileSet)).Image(tmpLargeTile.Index)
                ' 

                'If tmpLargeTile.TileSet <> 255 Then Libs.GetLib(GetImgLib(tmpLargeTile.TileSet)).PreloadImage(tmpLargeTile.Index)

                buffPos += 3

            Next
        Next

        '=== SMALL TILES ===

        ReDim bBuffer(mapStream.Length - mapStream.Position - 1)
        mapStream.Read(bBuffer, 0, (mapStream.Length - mapStream.Position))

        buffPos = 0
        Dim tmpRawSmallTile As RawSmallTile
        Dim tmpSmallTile As SmallTile

        ReDim mSmallTiles(mWidth - 1, mHeight - 1)

        For itX = 0 To mWidth - 1
            For itY = 0 To mHeight - 1

                tmpSmallTile = New SmallTile
                tmpRawSmallTile = EvoLib.Protocol.Functions.ArrayToStruct(bBuffer, GetType(RawSmallTile), buffPos)

                '3 = passable
                If tmpRawSmallTile.passable = 3 Then
                    tmpSmallTile.IsPassable = True
                Else
                    tmpSmallTile.IsPassable = False
                End If

                tmpSmallTile.TopTileSet = tmpRawSmallTile.TopSet
                tmpSmallTile.TopTileIndex = tmpRawSmallTile.TopTile
                If tmpSmallTile.TopTileSet = 255 Then
                    tmpSmallTile.TopTileIndex = 65535
                Else
                    CheckMissingTileSet(tmpRawSmallTile.TopSet, TileSetType.Top)
                End If
                If tmpSmallTile.TopTileIndex < 65535 Then tmpSmallTile.TopTileIndex += 1 ': TileSet(tmp3.TopTileSet).Image(tmp3.TopTileIndex).Precache()

                tmpSmallTile.BottomTileSet = tmpRawSmallTile.BottomSet
                tmpSmallTile.BottomTileIndex = tmpRawSmallTile.BottomTile
                If tmpSmallTile.BottomTileSet = 255 Then
                    tmpSmallTile.BottomTileIndex = 65535
                Else
                    CheckMissingTileSet(tmpRawSmallTile.BottomSet, TileSetType.Bottom)
                End If
                If tmpSmallTile.BottomTileIndex < 65535 Then tmpSmallTile.BottomTileIndex += 1 ': TileSet(tmp3.BottomTileSet).Image(tmp3.BottomTileIndex).Precache()

                'If tmp3.BottomTileSet <> 255 And tmp3.BottomTileSet <> 0 And tmp3.BottomTileSet <> 10 And tmp3.BottomTileSet <> 23 And tmp3.BottomTileSet <> 4 And tmp3.BottomTileSet <> 5 Then
                '    Debug.Print("###MAP###(" & x.ToString & "," & y.ToString & ") 0x" & Hex(tmp3.BottomTileSet) & " : " & tmp3.BottomTileIndex & "###")
                'End If

                tmpSmallTile.Ani1 = tmpRawSmallTile.Ani1
                tmpSmallTile.Ani2 = tmpRawSmallTile.Ani2
                tmpSmallTile.Door = tmpRawSmallTile.Door
                tmpSmallTile.DoorOff = tmpRawSmallTile.DoorOff
                tmpSmallTile.Light = tmpRawSmallTile.Light

                'If tmpSmallTile.BottomTileSet = 11 Then
                'Debug.Print("")
                'End If

                'If tmpSmallTile.Ani1 <> 255 Then
                'Debug.Print("")
                'End If

                'If tmpSmallTile.Light > 0 Then
                'Debug.Print("Light: " & tmpSmallTile.Light)
                'End If

                mSmallTiles(itX, itY) = tmpSmallTile
                'preloads tiles
                'If tmpSmallTile.BottomTileSet < 255 Then Libs.GetLib(GetImgLib(tmpSmallTile.BottomTileSet)).Image(tmpSmallTile.BottomTileIndex)
                'If tmpSmallTile.TopTileSet < 255 Then Libs.GetLib(GetImgLib(tmpSmallTile.TopTileSet)).Image(tmpSmallTile.TopTileIndex)

                'If tmpSmallTile.BottomTileSet <> 255 Then
                'If tmpSmallTile.BottomTileIndex <> 65535 Then
                'Libs.GetLib(GetImgLib(tmpSmallTile.BottomTileSet)).PreloadImage(tmpSmallTile.BottomTileIndex)
                'End If
                'End If

                'If tmpSmallTile.TopTileSet <> 255 Then
                'If tmpSmallTile.TopTileIndex <> 65535 Then
                'Libs.GetLib(GetImgLib(tmpSmallTile.TopTileSet)).PreloadImage(tmpSmallTile.TopTileIndex)
                'End If
                'End If

                buffPos += 14

            Next
        Next

        mapStream.Close()

    End Sub

    Public Sub UnloadXTiles(D As Direction)

        Dim xval As Integer
        Select Case D

            Case Direction.E, Direction.NE, Direction.SE
                xval = GameWindow.CoordX + GameWindow.DrawAreaStartOffsetX

            Case Direction.W, Direction.NW, Direction.SW
                xval = GameWindow.CoordX + GameWindow.DrawAreaStartOffsetX + GameWindow.DrawAreaTilesX

            Case Else
                Exit Sub

        End Select

        If xval < 0 Then xval = 0
        If xval >= mWidth Then xval = mWidth - 1

        Dim rX As Integer
        Dim rY As Integer
        Math.DivRem(xval, 2, rX)

        Dim min As Integer = (GameWindow.CoordY + GameWindow.DrawAreaStartOffsetY)
        Dim max As Integer = (GameWindow.CoordY + GameWindow.DrawAreaStartOffsetY + GameWindow.DrawAreaTilesY)
        If min < 0 Then min = 0
        If max >= mHeight Then max = mHeight - 1

        For yval As Integer = min To max

            With mSmallTiles(xval, yval)

                If .BottomTileSet <> 255 Then
                    If .BottomTileIndex <> 65535 Then
                        If Not GameWindow.HasDrawnTile(.BottomTileSet, .BottomTileIndex) Then
                            Libs.GetLib(GetImgLib(.BottomTileSet)).UnloadImage(.BottomTileIndex)
                        End If
                    End If
                End If

                If .TopTileSet <> 255 Then
                    If .TopTileIndex <> 65535 Then
                        If Not GameWindow.HasDrawnTile(.TopTileSet, .TopTileIndex) Then
                            Libs.GetLib(GetImgLib(.TopTileSet)).UnloadImage(.TopTileIndex)
                        End If
                    End If
                End If

            End With


            If rX = 0 Then
                Math.DivRem(yval, 2, rY)
                If rY = 0 Then
                    With GetLargeTile(xval, yval)
                        If .TileSet <> 255 Then
                            If Not GameWindow.HasDrawnTile(.TileSet, .Index) Then
                                Libs.GetLib(GetImgLib(.TileSet)).UnloadImage(.Index)
                            End If
                        End If
                    End With
                End If
            End If

        Next

    End Sub

    Public Sub UnloadYTiles(D As Direction)

        Dim yval As Integer
        Select Case D

            Case Direction.N, Direction.NE, Direction.NW
                yval = GameWindow.CoordY + GameWindow.DrawAreaStartOffsetY + GameWindow.DrawAreaTilesY

            Case Direction.S, Direction.SE, Direction.SW
                yval = GameWindow.CoordY + GameWindow.DrawAreaStartOffsetY

            Case Else
                Exit Sub

        End Select

        If yval < 0 Then yval = 0
        If yval >= mWidth Then yval = mWidth - 1

        Dim rX As Integer
        Dim rY As Integer
        Math.DivRem(yval, 2, rX)

        Dim min As Integer = (GameWindow.CoordX + GameWindow.DrawAreaStartOffsetX)
        Dim max As Integer = (GameWindow.CoordX + GameWindow.DrawAreaStartOffsetX + GameWindow.DrawAreaTilesX)
        If min < 0 Then min = 0
        If max >= mWidth Then max = mWidth - 1

        For xval As Integer = min To max

            With mSmallTiles(xval, yval)

                If .BottomTileSet <> 255 Then
                    If .BottomTileIndex <> 65535 Then
                        If Not GameWindow.HasDrawnTile(.BottomTileSet, .BottomTileIndex) Then
                            Libs.GetLib(GetImgLib(.BottomTileSet)).UnloadImage(.BottomTileIndex)
                        End If
                    End If
                End If

                If .TopTileSet <> 255 Then
                    If .TopTileIndex <> 65535 Then
                        If Not GameWindow.HasDrawnTile(.TopTileSet, .TopTileIndex) Then
                            Libs.GetLib(GetImgLib(.TopTileSet)).UnloadImage(.TopTileIndex)
                        End If
                    End If
                End If

            End With


            If rX = 0 Then
                Math.DivRem(xval, 2, rY)
                If rY = 0 Then
                    With GetLargeTile(xval, yval)
                        If .TileSet <> 255 Then
                            If Not GameWindow.HasDrawnTile(.TileSet, .Index) Then
                                Libs.GetLib(GetImgLib(.TileSet)).UnloadImage(.Index)
                            End If
                        End If
                    End With
                End If
            End If

        Next

    End Sub

    Private Enum TileSetType
        Large = 0
        Top = 1
        Bottom = 2
    End Enum

    Private Sub CheckMissingTileSet(ByRef i As Integer, t As TileSetType)

        Dim missing As Boolean

        If i > TileLibLookup.GetUpperBound(0) Then
            missing = True
        ElseIf TileLibLookup(i) = 0 Then
            missing = True
        End If

        If missing Then
            MsgBox("Missing Tile Set Found! #" & i & " (&H" & Hex(i) & ") Type: " & t.ToString)
        End If

    End Sub

End Class
